package com.skyline.energy.utils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.skyline.common.reflection.ArrayUtils;
import com.skyline.common.reflection.MethodUtils;
import com.skyline.common.reflection.TypeUtils;
import com.skyline.energy.definition.BatchDefinition;

/**
 * 参数解析、数值获取等操作工具类
 * 
 * @author wuqh
 * 
 */
public class ArgumentUtils {
	/**
	 * 通过一组getter方法，提取args中指定位置对象中相应属性的值。
	 * 
	 * @param getterMethods
	 * @param parameterIndexes
	 * @param args
	 * @param parameterNames
	 * @return
	 */
	public static Object[] fetchValues(Method[] getterMethods, Integer[] parameterIndexes, Object[] args,
			List<String> parameterNames) {
		Object[] values = new Object[getterMethods.length];
		for (int i = 0; i < getterMethods.length; i++) {
			Method method = getterMethods[i];
			Integer index = parameterIndexes[i];

			Object value = fetchValue(method, index, args, parameterNames);
			values[i] = value;
		}
		return values;
	}

	/**
	 * 通过getter方法提取对象中相应属性的值，其实就对args中指定index的对象的getter方法的一次反射调用，或者map的一次get操作。
	 * 
	 * @param getterMethod
	 *            getter方法
	 * @param index
	 *            args中的index
	 * @param args
	 *            方法调用的实际参数
	 * @param paramName
	 *            传入参数名称，这个在对象为Map时使用
	 * @return 提取到的值
	 */
	public static Object fetchValue(Method getterMethod, Integer index, Object[] args, String paramName) {
		if (index == null || index == -1) {
			return null;
		}

		Object arg = args[index];

		Object result = null;
		if (getterMethod == null) {
			result = fetchNonMethodValue(paramName, arg);
		} else {
			result = MethodUtils.invokeMethod(arg, getterMethod);
		}

		return getValueByType(result);
	}

	@SuppressWarnings("rawtypes")
	private static Object fetchNonMethodValue(String paramName, Object arg) {
		if (arg == null) {
			return null;
		}

		Class<?> clazz = arg.getClass();

		// 如果Map，执行"."操作就是执行get操作
		if (TypeUtils.isTypeMap(clazz)) {
			Map map = (Map) arg;
			int pos = paramName.indexOf('.');
			String prop = paramName.substring(pos + 1);
			return map.get(prop);
		}

		return arg;
	}

	private static Object getValueByType(Object result) {
		if (result == null) {
			return null;
		}

		// 如果是枚举类型需要转换为String，因为大多数据库都不识别这个类型。
		if (result.getClass().isEnum()) {
			return result.toString();
		} else {
			return result;
		}
	}

	/**
	 * 通过getter方法提取对象中相应属性的值，其实就对args中指定index的对象的getter方法的一次反射调用，或者map的一次get操作。
	 * 
	 * @param getterMethod
	 *            getter方法
	 * @param index
	 *            args中的index
	 * @param args
	 *            方法调用的实际参数
	 * @param parameterNames
	 *            传入参数名称，这个在对象为Map时使用
	 * @return 提取到的值
	 */
	public static Object fetchValue(Method getterMethod, Integer index, Object[] args, List<String> parameterNames) {
		if (index == null || index == -1) {
			return null;
		}

		String paramName = parameterNames.get(index);

		return fetchValue(getterMethod, index, args, paramName);
	}

	/**
	 * 获取批量SQL/Shell中需要使用的参数的List
	 * 
	 * @param args
	 * @param batchDefinition
	 * @return
	 */
	public static List<Object[]> generateBatchQueryArguments(Object[] args, BatchDefinition batchDefinition) {
		// 获取批量SQL/Shell的批数，即@BatchParam对应的几个数组的最小length

		int batchSize = caculateBatchSize(args, batchDefinition);

		List<Object[]> paramArrays = buildParamArrays(args, batchDefinition, batchSize);

		return paramArrays;
	}

	private static List<Object[]> buildParamArrays(Object[] args, BatchDefinition batchDefinition, int batchSize) {
		Integer[] batchParamIndexes = batchDefinition.getBatchParamIndexes();
		Method[] getterMethods = batchDefinition.getGetterMethods();
		Integer[] parameterIndexes = batchDefinition.getParameterIndexes();
		List<Object[]> paramArrays = new ArrayList<Object[]>(batchSize);
		List<String> parameterNames = batchDefinition.getParsedExpression().getParameterNames();

		// 每次克隆一组调用参数，然后用批量参数中这个批次中的批量参数值替换这个批量参数
		for (int i = 0; i < batchSize; i++) {
			Object[] cloneArgs = (Object[]) args.clone();

			for (int j = 0; j < batchParamIndexes.length; j++) {
				int index = batchParamIndexes[j];
				cloneArgs[index] = fetchArrayValue(cloneArgs[index], i);
			}

			Object[] paramArray = fetchValues(getterMethods, parameterIndexes, cloneArgs, parameterNames);
			paramArrays.add(paramArray);
		}
		return paramArrays;
	}

	private static int caculateBatchSize(Object[] args, BatchDefinition batchDefinition) {
		Integer[] batchParamIndexes = batchDefinition.getBatchParamIndexes();
		// 获取批量SQL/Shell的批数，即@BatchParam对应的几个数组的最小length
		int batchSize = -1;
		for (int i = 0; i < batchParamIndexes.length; i++) {
			int index = batchParamIndexes[i];
			if (batchSize == -1) {
				batchSize = ArrayUtils.getArrayOrListLength(args[index]);
			} else {
				batchSize = Math.min(batchSize, ArrayUtils.getArrayOrListLength(args[index]));
			}
		}
		return batchSize;
	}

	/**
	 * 提取array[index]或者list.get(index)的值
	 * 
	 * @param arrayOrList
	 * @param index
	 * @return
	 */
	private static Object fetchArrayValue(Object arrayOrList, int index) {
		if (arrayOrList == null) {
			return null;
		}

		Class<?> clazz = arrayOrList.getClass();
		boolean isArray = TypeUtils.isTypeArray(clazz);

		if (isArray) {
			Class<?> componentType = clazz.getComponentType();
			if (!componentType.isPrimitive()) {
				return ((Object[]) arrayOrList)[index];
			}

			return fetchPrimitiveArrayValue(arrayOrList, index, componentType);
		}

		boolean isList = TypeUtils.isTypeList(clazz);

		if (isList) {
			return ((List<?>) arrayOrList).get(index);
		}

		throw new IllegalArgumentException("arrayOrList参数必须为数组或者List的实现类");
	}

	/**
	 * 提取基本类型数组array[index]的值
	 * 
	 * @param array
	 * @param index
	 * @return
	 */
	private static Object fetchPrimitiveArrayValue(Object array, int index, Class<?> componentType) {
		if (long.class.equals(componentType)) {
			return ((long[]) array)[index];
		}
		if (int.class.equals(componentType)) {
			return ((int[]) array)[index];
		}
		if (boolean.class.equals(componentType)) {
			return ((boolean[]) array)[index];
		}
		if (double.class.equals(componentType)) {
			return ((double[]) array)[index];
		}
		if (char.class.equals(componentType)) {
			return ((char[]) array)[index];
		}
		if (float.class.equals(componentType)) {
			return ((float[]) array)[index];
		}
		if (byte.class.equals(componentType)) {
			return ((byte[]) array)[index];
		}
		if (short.class.equals(componentType)) {
			return ((short[]) array)[index];
		}
		return null;
	}
}
